﻿using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Legend.Mesh;
using Legend.Object;
using Legend.Component.Interface;
using System.Collections.Generic;
using Legend.Component.Common;
using System.IO;

namespace Legend.Component.Ground
{
    public class GroundRenderable : Interface.IRenderable
    {
        struct GroundStepInfo
        {
            public bool LeftPillar;
            public bool RightPillar;
            public char GroundType;
        }

        private GroundMesh mesh;
        private Effect effect = null;
        private Texture texture = null;
        private VertexDeclaration vertexDecl = null;
        private Queue<GameObject> pillarList = new Queue<GameObject>();
        private Model pillar;
        private BoundingBox pillarBoundingBox;
        private BoundingBox pillarBoundingBoxMirror;
        private Random random = new Random();
        private GameObject waterBlock;
        private GameObject bossWallBlock;
        private GameObject bridge;
        private GameObject bridgeLeftBlock;
        private GameObject bridgeRightBlock;
        private Queue<GroundStepInfo> groundDeployQueue = new Queue<GroundStepInfo>();
        private Queue<GameObject> pillarQueue = new Queue<GameObject>();

        public GroundRenderable()
        {
            mesh = new GroundMesh(0);
        }

        public void LoadResource(ContentManager contentManger)
        {
            effect = contentManger.Load<Effect>("GroundEffect");
            texture = contentManger.Load<Texture>("GroundMap");

            pillar = contentManger.Load<Model>("pillar");
            Helper.Mesh.SnapVertices(pillar, 1);

            pillarBoundingBox = Legend.Helper.Mesh.GetModelBoundingBox(pillar);
            pillarBoundingBoxMirror = pillarBoundingBox;
            Vector3 originMin = pillarBoundingBox.Min;
            Vector3 originMax = pillarBoundingBox.Max;
            originMax.Z -= originMin.Z;
            originMin.Z = 0;
            pillarBoundingBox.Min = originMin;
            pillarBoundingBox.Max = originMax;

            float temp;
            temp = originMin.X;
            originMin.X = -originMax.X;
            originMax.X = -temp;

            pillarBoundingBoxMirror.Max = originMax;
            pillarBoundingBoxMirror.Min = originMin;

            {
                waterBlock = new GameObject();
                SimpleRenderable render = waterBlock.CreateComponent<SimpleRenderable>();
                render.LoadModel(contentManger, "water");
                render.CastShadow = false;
                Helper.Mesh.SnapVertices(render.Model, 1);
                LinearMovement movement = waterBlock.CreateComponent<LinearMovement>();
                movement.Velocity = new Vector3(0, -100, 0);
                movement.Position = new Vector3(-10000, -10000, -100000);
                GameObjectManager.RegistGameObject(waterBlock);
            }
            
            {
                bossWallBlock = new GameObject();
                SimpleRenderable render = bossWallBlock.CreateComponent<SimpleRenderable>();
                render.LoadModel(contentManger, "wall");
                render.CastShadow = false;
                Helper.Mesh.SnapVertices(render.Model, 1);
                LinearMovement movement = bossWallBlock.CreateComponent<LinearMovement>();
                movement.Velocity = new Vector3(0, -100, 0);
                movement.Position = new Vector3(-10000, -10000, -100000);
                GameObjectManager.RegistGameObject(bossWallBlock);
            }

            {
                bridge = new GameObject();
                SimpleRenderable render = bridge.CreateComponent<SimpleRenderable>();
                render.LoadModel(contentManger, "bridge");
                Helper.Mesh.SnapVertices(render.Model, 1);
                render.CastShadow = false;
                Helper.Mesh.SnapVertices(render.Model, 1);
                LinearMovement movement = bridge.CreateComponent<LinearMovement>();
                movement.Velocity = new Vector3(0, -100, 0);
                movement.Position = new Vector3(-10000, -10000, -100000);
                GameObjectManager.RegistGameObject(bridge);
            }

            {
                bridgeLeftBlock = new GameObject();
                Collision collsion = bridgeLeftBlock.CreateComponent<Collision>();
                collsion.LocalBoundingBox = new BoundingBox(new Vector3(-400, -100, 0), new Vector3(-100, 100, 100));
                collsion.CollisionType = ICollision.CollisionTypes.Block;

                LinearMovement movement = bridgeLeftBlock.CreateComponent<LinearMovement>();
                movement.Velocity = new Vector3(0, -100, 0);
                movement.Position = new Vector3(-10000, -10000, -100000);
                GameObjectManager.RegistGameObject(bridgeLeftBlock);
            }

            {
                bridgeRightBlock = new GameObject();
                Collision collsion = bridgeRightBlock.CreateComponent<Collision>();
                collsion.LocalBoundingBox = new BoundingBox(new Vector3(100, -100, 0), new Vector3(400, 100, 100));
                collsion.CollisionType = ICollision.CollisionTypes.Block;

                LinearMovement movement = bridgeRightBlock.CreateComponent<LinearMovement>();
                movement.Velocity = new Vector3(0, -100, 0);
                movement.Position = new Vector3(-10000, -10000, -100000);
                GameObjectManager.RegistGameObject(bridgeRightBlock);
            }

            using (StreamReader reader = new StreamReader(File.Open("GroundDeploy.txt", FileMode.Open)))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    GroundStepInfo stepInfo;

                    if (line.Length > 2)
                    {
                        if (line[0] == '1')
                        {
                            stepInfo.LeftPillar = true;
                        }
                        else
                        {
                            stepInfo.LeftPillar = false;
                        }

                        if (line[2] == '1')
                        {
                            stepInfo.RightPillar = true;
                        }
                        else
                        {
                            stepInfo.RightPillar = false;
                        }

                        stepInfo.GroundType = line[1];
                        groundDeployQueue.Enqueue(stepInfo);
                    }
                }
            }
        }

        //private void RenderPillars(GraphicsDevice device)
        //{
        //    foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        //    {
        //        pass.Begin();
        //        foreach (ModelMesh mesh in pillar.Meshes)
        //        {
        //            foreach (ModelMeshPart part in mesh.MeshParts)
        //            {
        //                device.Vertices[0].SetSource(mesh.VertexBuffer, 0, part.VertexStride);
        //                device.Indices = mesh.IndexBuffer;

        //                device.VertexDeclaration = part.VertexDeclaration;
        //                device.DrawIndexedPrimitives(
        //                    PrimitiveType.TriangleList,
        //                    part.BaseVertex,
        //                    0,
        //                    part.NumVertices,
        //                    part.StartIndex,
        //                    part.PrimitiveCount);
        //            }
        //        }
        //        pass.End();
        //    }
        //}

        public override void Render(GraphicsDevice device)
        {
            device.RenderState.DepthBufferEnable = true;
            device.RenderState.AlphaBlendEnable = false;
//            device.RenderState.AlphaTestEnable = false;

            BoundingFrustum viewFrustum = new BoundingFrustum(ViewMatrix * ProjectionMatrix);
            LinearMovement groundMovement = Owner.FindComponent<IMovement>() as LinearMovement;
            BoundingBox nearestBox = mesh.NearestStep;

            nearestBox.Min = Vector3.Transform(nearestBox.Min, WorldMatrix);
            nearestBox.Max = Vector3.Transform(nearestBox.Max, WorldMatrix);

            if (viewFrustum.Intersects(nearestBox))
            {
                mesh.BuildBackward();
            }

            BoundingBox farestBox = mesh.FarestStep;

            farestBox.Min = Vector3.Transform(farestBox.Min, WorldMatrix);
            farestBox.Max = Vector3.Transform(farestBox.Max, WorldMatrix);
            if (viewFrustum.Intersects(farestBox))
            {
                while(pillarList.Count > 0)
                {
                    GameObject pillartObject = pillarList.Peek();
                    IMovement movement = pillartObject.FindComponent<IMovement>();
                    BoundingBox boundingBox = pillarBoundingBox;
                    boundingBox.Min += movement.Position;
                    boundingBox.Max += movement.Position;

                    if (!viewFrustum.Intersects(boundingBox))
                    {
                        GameObjectManager.UnregistGameObject(pillarList.Dequeue());
                    }
                    else
                    {
                        break;
                    }
                }

                Vector3 groundPosition;

                
                GroundStepInfo stepInfo = groundDeployQueue.Dequeue();
                mesh.BuildForward(stepInfo.GroundType, out groundPosition);

                if (stepInfo.GroundType == 'W')
                {
                    Vector3 newPosition = Vector3.Transform(groundPosition, WorldMatrix);
                    IMovement movement = waterBlock.FindComponent<IMovement>();
                    movement.Position = newPosition;

                    movement = bridge.FindComponent<IMovement>();
                    movement.Position = newPosition;

                    movement = bridgeRightBlock.FindComponent<IMovement>();
                    movement.Position = newPosition;

                    movement = bridgeLeftBlock.FindComponent<IMovement>();
                    movement.Position = newPosition;
                }
                else if (stepInfo.GroundType == 'A')
                {
                    IMovement movement = bossWallBlock.FindComponent<IMovement>();
                    Vector3 newPosition = Vector3.Transform(groundPosition, WorldMatrix);
                    newPosition.Z -= 100f;
                    newPosition.Y -= 500f;
                    movement.Position = newPosition;

                }

                if (stepInfo.RightPillar)
                {
                    GameObject pillarRight = new GameObject();
                    SimpleRenderable render = pillarRight.CreateComponent<SimpleRenderable>();
                    render.Model = pillar;
                    render.CastShadow = false;
                    LinearMovement movement = pillarRight.CreateComponent<LinearMovement>();
                    movement.Position = new Vector3(350, farestBox.Max.Y, 0);
                    movement.Velocity = groundMovement.Velocity;
                    Collision collision = pillarRight.CreateComponent<Collision>();
                    collision.LocalBoundingBox = pillarBoundingBox;
                    collision.CollisionType = ICollision.CollisionTypes.Block;

                    pillarList.Enqueue(pillarRight);
                    GameObjectManager.RegistGameObject(pillarRight);
                }

                if (stepInfo.LeftPillar)
                {
                    GameObject pillarLeft = new GameObject();
                    SimpleRenderable render = pillarLeft.CreateComponent<SimpleRenderable>();
                    render.Model = pillar;
                    render.CastShadow = false;
                    LinearMovement movement = pillarLeft.CreateComponent<LinearMovement>();
                    movement.Rotate(Vector3.Backward, (float)Math.PI);
                    movement.Position = new Vector3(-350, farestBox.Max.Y, 0);
                    movement.Velocity = groundMovement.Velocity;
                    Collision collision = pillarLeft.CreateComponent<Collision>();
                    collision.LocalBoundingBox = pillarBoundingBoxMirror;
                    collision.CollisionType = ICollision.CollisionTypes.Block;

                    pillarList.Enqueue(pillarLeft);
                    GameObjectManager.RegistGameObject(pillarLeft);
                }

                if (groundDeployQueue.Count == 0)
                {
                    LinearMovement movement = Owner.FindComponent<IMovement>() as LinearMovement;
                    movement.Velocity = Vector3.Zero;

                    while(pillarList.Count > 0)
                    {
                        GameObject pillar = pillarList.Dequeue();
                        movement = pillar.FindComponent<IMovement>() as LinearMovement;
                        movement.Velocity = Vector3.Zero;
                    }

                    movement = bridgeLeftBlock.FindComponent<IMovement>() as LinearMovement;
                    movement.Velocity = Vector3.Zero;
                    movement = bridgeRightBlock.FindComponent<IMovement>() as LinearMovement;
                    movement.Velocity = Vector3.Zero;
                    movement = bridge.FindComponent<IMovement>() as LinearMovement;
                    movement.Velocity = Vector3.Zero;
                    movement = waterBlock.FindComponent<IMovement>() as LinearMovement;
                    movement.Velocity = Vector3.Zero;
                    movement = bossWallBlock.FindComponent<IMovement>() as LinearMovement;
                    movement.Velocity = Vector3.Zero;
                }
            }

            if (vertexDecl == null || vertexDecl.GraphicsDevice != device)
            {
                vertexDecl = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
            }
            effect.CurrentTechnique = effect.Techniques["Technique1"];

            effect.Begin();
            effect.Parameters["Texture"].SetValue(texture);
            effect.Parameters["World"].SetValue(WorldMatrix);
            effect.Parameters["Projection"].SetValue(ProjectionMatrix);
            effect.Parameters["View"].SetValue(ViewMatrix);
            
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                device.VertexDeclaration = vertexDecl;
//                device.DrawUserPrimitives(PrimitiveType.LineList, mesh.VertexData, mesh.VertexOffset, mesh.VertexNumber / 2);
                device.DrawUserIndexedPrimitives(mesh.PrimitiveType, mesh.VertexData, mesh.VertexOffset, mesh.VertexNumber, mesh.IndexData, mesh.IndexOffset, mesh.PrimitiveCount);

                pass.End();
            }
            effect.End();

            //int count = 0;
            //Quaternion rotate = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), 3.14f);

            //List<GameObject> newList = new List<GameObject>();

            //foreach (GameObject pillarObject in pillarList)
            //{
            //    Matrix worldMatrix = WorldMatrix;
            //    worldMatrix.M42 = worldLowerRight.Y + 200 * count;
            //    worldMatrix.M43 -= 122;

            //    IRenderable renderable = pillarObject.FindComponent<IRenderable>();
            //    if (count % 2 == 0)
            //    {
            //        renderable.WorldMatrix = worldMatrix;
            //    }
            //    else
            //    {
            //        renderable.WorldMatrix = Matrix.CreateFromQuaternion(rotate) * worldMatrix;
            //    }
            //    ++count;

            //    ICollision collision = pillarObject.FindComponent<ICollision>();
            //    BoundingBox boundingBox = pillarBoundingBox;
            //    boundingBox.Max += worldMatrix.Translation;
            //    boundingBox.Min += worldMatrix.Translation;
            //    collision.BoundingBox = boundingBox;
            //    if (viewFrustum.Intersects(boundingBox))
            //    {
            //        newList.Add(pillarObject);
            //    }
            //}

            //pillarList = newList;
        }
    }
}
